package nl.utwente.ewi.hmi.multitouch.drivers.calibrate;

import java.awt.geom.AffineTransform;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import nl.utwente.ewi.hmi.multitouch.TouchDevice;
import nl.utwente.ewi.hmi.multitouch.TouchDeviceInfo;
import nl.utwente.ewi.hmi.multitouch.io.TouchDeviceException;
import nl.utwente.ewi.hmi.multitouch.io.TouchStream;
import nl.utwente.ewi.hmi.multitouch.io.TouchStreamProvider;

public class CalibratedTouchStreamProvider implements TouchStreamProvider {

	private TouchStreamProvider wrappedTouchStreamProvider = null;
	
	private List<TouchDeviceInfo> calibratableTouchDevices = null;
	
	private TouchDeviceCalibrator calibrator = null;
	
	private Map<TouchDeviceInfo, TouchStream> calibratedTouchStreams = null;
	
	public CalibratedTouchStreamProvider(TouchStreamProvider wrappedTouchStreamProvider, TouchDeviceCalibrator calibrator) {
		this.wrappedTouchStreamProvider = wrappedTouchStreamProvider;
		this.calibrator = calibrator;
		this.calibratedTouchStreams = new HashMap<TouchDeviceInfo, TouchStream>();
	}

	@Override
	public List<TouchDeviceInfo> getTouchDeviceInfoList() {
		if(this.calibratableTouchDevices == null) {
			this.calibratableTouchDevices = new LinkedList<TouchDeviceInfo>();

			for(TouchDeviceInfo wrappedTouchDeviceInfo : wrappedTouchStreamProvider.getTouchDeviceInfoList()) {
				this.calibratableTouchDevices.add(new CalibratedTouchDeviceInfo(wrappedTouchDeviceInfo));
			}
		}
		return this.calibratableTouchDevices;
	}

	@Override
	public TouchStream getTouchStream(TouchDeviceInfo deviceInfo) {
		if(!(deviceInfo instanceof CalibratedTouchDeviceInfo)) {
			return null;
		}

		CalibratedTouchDeviceInfo calibratedTouchDeviceInfo = (CalibratedTouchDeviceInfo) deviceInfo;
		
		TouchDeviceInfo wrappedTouchDeviceInfo = calibratedTouchDeviceInfo.getWrappedTouchDeviceInfo();
		
		if(this.calibratedTouchStreams.containsKey(deviceInfo)) {
			return this.calibratedTouchStreams.get(deviceInfo);
		}

		TouchStream touchStream = wrappedTouchStreamProvider.getTouchStream(wrappedTouchDeviceInfo);
		
		TouchDevice touchDevice = new TouchDevice(touchStream);
		
		try {
			touchDevice.start();
		} catch (TouchDeviceException e) {
		}
		
		AffineTransform transform = calibrator.calibrate(touchDevice);
		
		try {
			touchDevice.stop();
		} catch (TouchDeviceException e) {
		}
		
		calibratedTouchDeviceInfo.setTransform(transform);
		
		TouchStream calibratedTouchStream = new CalibratedTouchStream(touchStream, calibratedTouchDeviceInfo);
		
		this.calibratedTouchStreams.put(calibratedTouchDeviceInfo, calibratedTouchStream);

		return calibratedTouchStream;
	}

}
